use crate::block::Block;
use crate::blockhandle::BlockHandle;
use crate::crc;
use crate::env::RandomAccess;
use crate::error::{err, Result, StatusCode};
use crate::filter;
use crate::filter_block::FilterBlockReader;
use crate::log::unmask_crc;
use crate::options::Options;
use crate::table_builder;

use integer_encoding::FixedInt;

/// Reads the data for the specified block handle from a file.
fn read_bytes_from_file(f: &dyn RandomAccess, offset: usize, size: usize) -> Result<Vec<u8>> {
    let mut buf = vec![0; size];
    let bytes_read = f.read_at(offset, &mut buf)?;
    if bytes_read != size {
        return err(
            StatusCode::IOError,
            "Failed to read complete data from file",
        );
    }
    Ok(buf)
}

/// Reads a serialized filter block from a file and returns a FilterBlockReader.
pub fn read_filter_block(
    src: &dyn RandomAccess,
    location: &BlockHandle,
    policy: filter::BoxedFilterPolicy,
) -> Result<FilterBlockReader> {
    if location.size() == 0 {
        return err(
            StatusCode::InvalidArgument,
            "no filter block in empty location",
        );
    }
    let buf = read_bytes_from_file(src, location.offset(), location.size())?;
    Ok(FilterBlockReader::new_owned(policy, buf))
}

/// Reads a table block from a random-access source.
/// A table block consists of [bytes..., compress (1B), checksum (4B)]; the handle only refers to
/// the location and length of [bytes...].
///
/// 这个 read_table_block 函数是SSTable 数据块的读取和解析工具，负责从磁盘文件中读取指定位置的数据块（包括原始数据、压缩标识和校验和），
/// 并执行校验、解压缩等操作，最终返回可直接使用的 Block 结构体。它是将磁盘上的原始字节数据转换为内存中可用数据块的关键步骤。
///
/// read_table_block 函数的核心价值在于实现数据块从磁盘到内存的完整转换，包含以下关键能力：
///
/// 完整读取数据：按 SSTable 格式读取数据块及其元数据（压缩类型、校验和）；
///
/// 数据完整性校验：通过校验和确保数据在存储或传输过程中未被损坏；
///
/// 解压缩处理：根据存储时使用的压缩算法，恢复原始数据；
///
/// 封装为可用结构：将原始字节转换为可直接操作的 Block 结构体，供上层查询逻辑使用。
pub fn read_table_block(
    opt: Options,
    f: &dyn RandomAccess,
    location: &BlockHandle,
) -> Result<Block> {
    let data_size = location.size();  // 数据块原始大小（压缩后）
    let trailer_size =
        table_builder::TABLE_BLOCK_COMPRESS_LEN + table_builder::TABLE_BLOCK_CKSUM_LEN; // 尾部元数据大小（压缩类型1字节 + 校验和4字节）
    let total_read_size = data_size + trailer_size; // 总读取大小
    // SSTable 的数据块在磁盘上存储格式为：[压缩数据][压缩类型(1字节)][校验和(4字节)]；
    // 计算需要读取的总字节数（数据 + 尾部元数据），并分配对应大小的缓冲区。
    let mut combined_buf = vec![0; total_read_size]; // 分配缓冲区
    // 通过 read_at 从文件的 location.offset() 位置读取 total_read_size 字节到缓冲区；
    // 校验读取的字节数是否符合预期，确保完整读取数据块及其元数据。
    let bytes_read = f.read_at(location.offset(), &mut combined_buf)?;

    if bytes_read != total_read_size {
        return err(StatusCode::IOError, "Failed to read complete block and trailer", );
    }
    // 将缓冲区分割为三个部分：压缩数据、压缩类型标识、校验和。
    let block_data_slice = &combined_buf[0..data_size]; // 压缩的数据部分
    let compress_type = combined_buf[data_size];  // 压缩类型（1字节）
    let cksum_slice =
        &combined_buf[data_size + table_builder::TABLE_BLOCK_COMPRESS_LEN..total_read_size]; // 校验和部分（4字节）
    // 解析存储的校验和（expected_cksum）；
    let expected_cksum = unmask_crc(u32::decode_fixed(cksum_slice));  // 解析预期校验和
    // 调用 verify_table_block 计算当前数据块的实际校验和，并与预期值对比：
    // 若不匹配，返回 “数据损坏” 错误（可能是文件损坏或磁盘错误）；
    // 若匹配，继续处理。
    if !verify_table_block(block_data_slice, compress_type, expected_cksum) {
        return err(StatusCode::Corruption, &format!("checksum verification failed for block at {}", location.offset()), );
    }
    // 从配置中获取支持的压缩算法列表（compressor_list）；
    // 根据 compress_type 选择对应的解压缩算法，对 block_data_slice 进行解压缩，得到原始数据（未压缩）。
    let compressor_list = opt.compressor_list.clone();

    // The compressor's decode method expects a Vec<u8>.
    // If block_data_slice is empty, to_vec() is fine.
    let decoded_data = compressor_list.get(compress_type)?.decode(block_data_slice.to_vec())?;

    Ok(Block::new(opt, decoded_data.into()))
}

/// Verify checksum of block
/// 这个 verify_table_block 函数是数据块完整性校验工具，用于验证从磁盘读取的 SSTable 数据块是否完整、未被篡改或损坏。它通过校验和（checksum）比对，确保数据块在存储或传输过程中没有发生错误。
///
/// data: &[u8]：数据块的原始字节（通常是压缩后的字节）；
///
/// compression: u8：数据块使用的压缩算法标识；
///
/// want: u32：存储在磁盘上的预期校验和值。
fn verify_table_block(data: &[u8], compression: u8, want: u32) -> bool {
    // 创建一个 CRC 校验和计算器（digest），用于计算数据的校验和。
    let mut digest = crc::digest();
    digest.update(data); // 计算数据块本身的校验和
    digest.update(&[compression; 1]); // 加入压缩类型的校验（1字节）
    // 计算最终的校验和（digest.finalize()），并与磁盘上存储的预期校验和（want）比对；
    // 若相等，返回 true（校验通过，数据完整）；否则返回 false（校验失败，数据可能损坏）。
    digest.finalize() == want
}
